use super::{
    cbase::CBase, err, err_value, get_db, json, ok, throw_err, Base, Client, ECode, MapSv, Resp,
    VBase, VMap,
};

pub struct Md;

impl Base for Md {}
impl CBase for Md {}

impl Md {
    pub async fn do_cmd(&self, cmd: &str, data: &MapSv, client: &Client) -> Resp {
        match cmd {
            "md" => self.md(data, client).await,
            "rm" => self.rm(data, client).await,
            "mdchunk" => self.md_chunk(data, client).await,
            _ => throw_err!(ECode::NoServe, "no serve"),
        }
    }

    pub fn get_data(&self, data: &MapSv, ret: &mut Vec<String>) -> Resp {
        let tab = data.ck_str_empty_trim("n")?;
        let fld = data.ck_array("data")?;

        for it in fld.iter() {
            let it = it.ckobj()?;
            let tm = it.ck_str_empty_trim("tm")?;
            let tag = it.ck_obj("k")?;
            let val = it.ck_obj("v")?;

            let q = self.tag_field(
                &json!({
                    "tab": tab,
                    "time": tm,
                    "tag": tag,
                    "field": val,
                }),
                false,
                0,
                false,
            )?;

            ret.push(q);
        }

        ok!()
    }

    pub async fn md_chunk(&self, data: &MapSv, client: &Client) -> Resp {
        let chunk = data.ck_array("chunk")?;

        let mut ret = Vec::new();

        for it in chunk.iter() {
            let it = it.ckobj()?;
            self.get_data(it, &mut ret)?;
        }

        if ret.is_empty() {
            throw_err!(ECode::EmptyData, "chunk error");
        }

        let db = get_db(data);
        for it in ret.chunks(1000) {
            let q = it.join("\n");
            client.send_write(&q, &db).await?;
        }

        ok!()
    }

    pub async fn md(&self, data: &MapSv, client: &Client) -> Resp {
        let mut ret = Vec::new();

        self.get_data(data, &mut ret)?;

        if ret.is_empty() {
            throw_err!(ECode::EmptyData, "chunk error");
        }

        let db = get_db(data);
        for it in ret.chunks(1000) {
            let q = it.join("\n");
            client.send_write(&q, &db).await?;
        }

        ok!()
    }

    pub async fn rm(&self, data: &MapSv, client: &Client) -> Resp {
        let tab = data.ck_str_empty_trim("n")?;

        let opt = match data.ck_str_empty_trim("opt")? {
            "eq" => "=",
            "le" => "<=",
            "ge" => ">=",
            "lt" => "<",
            "gt" => ">",
            _ => throw_err!(ECode::Para, "opt error"),
        };

        let mut time = data.ck_str_empty_trim("time")?.to_string();

        if let Some(utc) = data.k_f64("utc") {
            time = match self.utc_offset(&time, utc, None) {
                Some(v) => v,
                None => throw_err!(ECode::Time, "utc error"),
            };
        } else if let Some(utc) = data.k_i64("utc") {
            time = match self.utc_offset(&time, utc as f64, None) {
                Some(v) => v,
                None => throw_err!(ECode::Time, "utc error"),
            };
        }

        let db = get_db(data);
        let ret = client.rm_row(tab, opt, &time, &db).await?;
        let ret = ret.ck_first_obj("results")?;

        match ret.k_str("error") {
            Some(v) => throw_err!(ECode::Para, v),
            None => ok!(),
        }
    }
}
